using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Win32;

namespace TopMostHost
{
    class Program
    {
        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        [DllImport("user32.dll")]
        static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll")]
        static extern IntPtr GetAncestor(IntPtr hWnd, uint gaFlags);

        [DllImport("user32.dll")]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
        const int GWL_EXSTYLE = -20;
        const int WS_EX_TOPMOST = 0x00000008;
        const uint SWP_NOMOVE = 0x0001;
        const uint SWP_NOSIZE = 0x0002;
        const uint SWP_NOACTIVATE = 0x0010;
        const uint GA_ROOT = 2;
        const uint GA_ROOTOWNER = 3;

        static void Main(string[] args)
        {
            var input = Console.OpenStandardInput();
            var lengthBytes = new byte[4];
            var r = input.Read(lengthBytes, 0, 4);
            if (r == 0) return;
            var length = BitConverter.ToInt32(lengthBytes, 0);
            var data = new byte[length];
            var read = 0;
            while (read < length)
            {
                var n = input.Read(data, read, length - read);
                if (n <= 0) break;
                read += n;
            }
            var payload = Encoding.UTF8.GetString(data);
            string command = "toggle";
            string extensionId = "";
            string manifestPathArg = "";
            try
            {
                using var doc = JsonDocument.Parse(payload);
                if (doc.RootElement.TryGetProperty("command", out var cmdProp))
                {
                    command = cmdProp.GetString() ?? "toggle";
                }
                if (doc.RootElement.TryGetProperty("extension_id", out var idProp))
                {
                    extensionId = idProp.GetString() ?? "";
                }
                if (doc.RootElement.TryGetProperty("manifest_path", out var mpProp))
                {
                    manifestPathArg = mpProp.GetString() ?? "";
                }
            }
            catch { }

            if (string.Equals(command, "setup", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    var regPath = "HKEY_CURRENT_USER\\Software\\Google\\Chrome\\NativeMessagingHosts\\com.topmost.chrome_window";
                    var manifestPath = manifestPathArg;
                    if (string.IsNullOrWhiteSpace(manifestPath))
                    {
                        var regVal = Registry.GetValue(regPath, null, null) as string;
                        manifestPath = !string.IsNullOrWhiteSpace(regVal) ? regVal :
                            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
                                "git_code", "google浏览器窗口置顶插件", "native", "com.topmost.chrome_window.json");
                        if (!File.Exists(manifestPath))
                        {
                            var repoDefault = Path.Combine(AppContext.BaseDirectory, "com.topmost.chrome_window.json");
                            manifestPath = repoDefault;
                        }
                    }

                    Registry.SetValue(regPath, null, manifestPath);

                    if (!File.Exists(manifestPath))
                    {
                        WriteResponse(new { ok = false, status = "MANIFEST_NOT_FOUND", manifestPath });
                        return;
                    }

                    var json = File.ReadAllText(manifestPath, Encoding.UTF8);
                    var node = JsonNode.Parse(json) as JsonObject;
                    if (node == null)
                    {
                        WriteResponse(new { ok = false, status = "MANIFEST_PARSE_ERROR" });
                        return;
                    }

                    var arr = node["allowed_origins"] as JsonArray;
                    if (arr == null)
                    {
                        arr = new JsonArray();
                        node["allowed_origins"] = arr;
                    }
                    var origin = "chrome-extension://" + (extensionId?.Trim() ?? "") + "/";
                    if (!string.IsNullOrWhiteSpace(extensionId))
                    {
                        var exists = false;
                        foreach (var item in arr)
                        {
                            if (string.Equals(item?.ToString(), origin, StringComparison.OrdinalIgnoreCase))
                            {
                                exists = true; break;
                            }
                        }
                        if (!exists) arr.Add(origin);
                    }

                    var exeExpected = Path.Combine(AppContext.BaseDirectory, "TopMostHost.exe");
                    node["path"] = exeExpected.Replace("\\", "\\\\");

                    File.WriteAllText(manifestPath, node.ToJsonString(new JsonSerializerOptions { WriteIndented = true }), Encoding.UTF8);
                    WriteResponse(new { ok = true, status = "SETUP_DONE", manifestPath, origin });
                    return;
                }
                catch
                {
                    WriteResponse(new { ok = false, status = "SETUP_ERROR" });
                    return;
                }
            }

            var hwnd = GetForegroundWindow();
            var status = "UNKNOWN";
            if (hwnd != IntPtr.Zero)
            {
                var rootOwner = GetAncestor(hwnd, GA_ROOTOWNER);
                if (rootOwner != IntPtr.Zero) hwnd = rootOwner; else {
                    var root = GetAncestor(hwnd, GA_ROOT);
                    if (root != IntPtr.Zero) hwnd = root;
                }
                GetWindowThreadProcessId(hwnd, out var pid);
                try
                {
                    var p = Process.GetProcessById((int)pid);
                    if (!string.Equals(p.ProcessName, "chrome", StringComparison.OrdinalIgnoreCase))
                    {
                        WriteResponse(new { ok = false, status = "NOT_CHROME" });
                        return;
                    }
                }
                catch
                {
                    WriteResponse(new { ok = false, status = "PROCESS_ERROR" });
                    return;
                }

                var ex = GetWindowLong(hwnd, GWL_EXSTYLE);
                var isTop = (ex & WS_EX_TOPMOST) != 0;
                if (string.Equals(command, "query", StringComparison.OrdinalIgnoreCase))
                {
                    ex = GetWindowLong(hwnd, GWL_EXSTYLE);
                    status = (ex & WS_EX_TOPMOST) != 0 ? "TOPMOST" : "NOTOPMOST";
                }
                else if (string.Equals(command, "set", StringComparison.OrdinalIgnoreCase))
                {
                    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
                    ex = GetWindowLong(hwnd, GWL_EXSTYLE);
                    status = (ex & WS_EX_TOPMOST) != 0 ? "TOPMOST" : "NOTOPMOST";
                }
                else if (string.Equals(command, "unset", StringComparison.OrdinalIgnoreCase))
                {
                    SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
                    ex = GetWindowLong(hwnd, GWL_EXSTYLE);
                    status = (ex & WS_EX_TOPMOST) != 0 ? "TOPMOST" : "NOTOPMOST";
                }
                else
                {
                    if (isTop)
                    {
                        SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
                        ex = GetWindowLong(hwnd, GWL_EXSTYLE);
                        status = (ex & WS_EX_TOPMOST) != 0 ? "TOPMOST" : "NOTOPMOST";
                    }
                    else
                    {
                        SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
                        ex = GetWindowLong(hwnd, GWL_EXSTYLE);
                        status = (ex & WS_EX_TOPMOST) != 0 ? "TOPMOST" : "NOTOPMOST";
                    }
                }
                WriteResponse(new { ok = true, status });
                return;
            }
            WriteResponse(new { ok = false, status = "NO_WINDOW" });
        }

        static void WriteResponse(object obj)
        {
            var json = JsonSerializer.Serialize(obj);
            var bytes = Encoding.UTF8.GetBytes(json);
            var length = BitConverter.GetBytes(bytes.Length);
            Console.OpenStandardOutput().Write(length, 0, 4);
            Console.OpenStandardOutput().Write(bytes, 0, bytes.Length);
            Console.OpenStandardOutput().Flush();
        }
    }
}
